#include "writable_file.h"

namespace DB_NAME {

// flush的数据可能会停留在内核的缓冲区，fdatasync可以保证数据已经写入磁盘
RC WritableFile::sync_fd() 
{
  return ::fdatasync(fd_) == 0? RC::SUCCESS : RC::SYNC_ERROR;
}

// 向下flush数据的实际操作
RC WritableFile::write_unbuffered(const char* data, size_t size) 
{
  RC ret = RC::SUCCESS;
  while (IS_SUCC && size > 0) {
    ssize_t written_size = write(fd_, data, size);
    if (UNLIKELY(written_size < 0)) {
      if (errno == EINTR) {
        continue;
      }
      ret = IO_ERROR;
    } else {
      data += written_size;
      size -= written_size;
    }
  }
  return ret;
}

// 将缓冲区的数据下刷，pos回0
RC WritableFile::flush_buffer() 
{
  RC ret = RC::SUCCESS;
  if (OP_FAIL(write_unbuffered(buf_, pos_))) {
    GLOB_LOG_ERROR("Do write_unbuffered error, ret={}", KR(ret));
  } else {
    pos_ = 0;
  }
  return ret;
}

// 向文件写数据
// 优先将数据写入缓冲区，超过buffer大小时下刷，当数据过大时不再拷贝入缓冲区，直接下刷
RC WritableFile::Append(const SView &data_view, bool sync = false) 
{
  const char *data = data_view.data();
  size_t size = data_view.size();
  RC ret = RC::SUCCESS;

  if (sync || UNLIKELY(size > 2 * kWritableBufferSize - pos_)) {
    if (OP_FAIL(flush_buffer())) {
      GLOB_LOG_ERROR("Fail to flush buffer, ret={}", KR(ret));
    } else if (OP_FAIL(write_unbuffered(data, size))) {
      GLOB_LOG_ERROR("Fail to write_unbuffered in direct, ret={}", KR(ret));
    }
  } else {
    size_t copy_size = std::min(size, kWritableBufferSize - pos_);
    copy_to_buffer(data, copy_size);
    size -= copy_size;
    if (size > 0) {
      if (OP_FAIL(flush_buffer())) {
        GLOB_LOG_ERROR("Fail to flush buffer, ret={}", KR(ret));
      } else {
        copy_to_buffer(data + copy_size, size);
      }
    }
  }
  return ret;
}

// 关闭WritableFile
RC WritableFile::Close() 
{
  RC ret = RC::SUCCESS;
  if (OP_FAIL(flush_buffer())) {
    GLOB_LOG_ERROR("Fail to flush buffer, ret={}", KR(ret));
  } else if (close(fd_) < 0) {
    ret = RC::IO_ERROR;
    GLOB_LOG_ERROR("Fail to close file, ret={}", KR(ret));
  } else {
    fd_ = -1;
  }
  return ret;
}

// 确保写入的数据都已经落盘
RC WritableFile::Sync() 
{
  RC ret = RC::SUCCESS;
  if (OP_FAIL(flush_buffer())) {
    GLOB_LOG_ERROR("Fail to flush buffer, ret={}", KR(ret));
  } else if (OP_FAIL(sync_fd())) {
    GLOB_LOG_ERROR("Fail to sync fd, ret={}", KR(ret));
  }
  return ret;
}

}